var app = null, google, $;

var modConstants = {
	VERSION: 0.3,
	VERSION_SPAN: "span#version",
	DEFAULT_PANE: "div#default",
	AUTHENTICATED_PANE: "div#authenticated",
	EVENTS: "div#events",
	//STATUS: "div#status",
	CRT_WEEK: "span.crtWeek",
	CRT_WEEK_TOTAL: "span.crtWeekTotal",
	LOGIN: "#login",
	LOGOUT: "#logout",
	PREV_WEEK: ".prevWeek",
	NEXT_WEEK: ".nextWeek",
	MAX_RESULTS: 1000
	//EVENT_SELECT: "event-select",
	//WEEK_PANE: "week-pane",
};

var modConfiguration = {
	// Location of the event feed of the primary calendar for the authenticated user
	EVENT_FEED_URL: "https://www.google.com/calendar/feeds/default/private/full",
	HOSTED_DOMAIN: "" //let empty to get a domain choice list
};

var modData = {
	startDate: null, 
	stopDate: null,
	sheets: null,	
	init: function (aStartDate, aStopDate) {
		var aDate = null;
		modData.startDate = aStartDate;
		modData.stopDate = aStopDate;
		modData.sheets = [];
		for (aDate = modData.startDate; aDate < modData.stopDate; aDate = aDate.addDays(1)) {
			modData.sheets.push({
				sheetDate: aDate, 
				projects: []
			});
		}
	},
	sheetIndex: function (aDate) {
		var i = 0;
		for (i = 0; i < modData.sheets.length; i = i + 1) {
			if (modData.sheets[i].sheetDate.sameDate(aDate)) { return i; }
		}
		return -1;
	},
	sheet: function (aDate) {
		return modData.sheets[modData.sheetIndex(aDate)];
	},
	findProject: function (aDate, aProject) {
		var i = 0, sheet = modData.sheet(aDate);
		for (i = 0; i < sheet.projects.length; i = i + 1) {
			if (sheet.projects[i].name.toLowerCase() === aProject.toLowerCase()) { return sheet.projects[i]; }
		}
		return null;
	},
	addProject: function (aDate, aProject) {
		var sheet = modData.sheet(aDate);
		sheet.projects.push({
			name: aProject, 
			tasks: []
		});
		return modData.findProject(aDate, aProject);
	},
	addTask: function (task) {
		//var sheet = modData.sheet(task.startDate);
		var proj = modData.findProject(task.startDate, task.project);
		if (proj === null) {
			proj = modData.addProject(task.startDate, task.project);
		}
		proj.tasks.push(task);
	}	
};

var modCalendar = {
	/**
	 * Sets the global calendar service to a new instance.  Also resets the form 
	 * fields to clear out any information that may have been cached.
	 */
	init: function () {
		google.gdata.client.init(app.calendar.doOnHandleError);
		var token = google.accounts.user.checkLogin(app.configuration.EVENT_FEED_URL);
		// A google.gdata.calendar.CalendarService object that can be used to access private feed using AuthSub.
		this.myService = new google.gdata.calendar.CalendarService("gCal-project-time-" + app.constants.VERSION);
		// check if possible to show app info !!!
		if (google.accounts.user.checkLogin(app.configuration.EVENT_FEED_URL)) {
			$(app.constants.DEFAULT_PANE).hide();
			$(app.constants.AUTHENTICATED_PANE).show();
			app.presentation.showCurrentWeek();
		} else {
			$(app.constants.AUTHENTICATED_PANE).hide();
			$(app.constants.DEFAULT_PANE).show();	
		} 
		app.calendar.doOnReset();
	},
	/**
	 * Submits a query for all events that occur today.
	 */
	getEvents: function (startDate, endDate) {
		var query, startmin, startmax;
		modData.init(startDate, endDate);
		query = new google.gdata.calendar.CalendarEventQuery(app.configuration.EVENT_FEED_URL);
		query.setMaxResults(app.constants.MAX_RESULTS);
		startmin = new google.gdata.DateTime(startDate, false);
		query.setMinimumStartTime(google.gdata.DateTime.toIso8601(startmin));
		startmax = new google.gdata.DateTime(endDate, false);
		query.setMaximumStartTime(google.gdata.DateTime.toIso8601(startmax));
		//debug: google.gdata.util.log("uri=" + query.getUri());
		modCalendar.myService.getEventsFeed(query, app.calendar.handleEventsFeed, app.calendar.doOnHandleError);
	},

	/**
	 * Populates the dropdown menu with events returned in the query for
	 * today's events.
	 *
	 * @param {JSON} The JSON object returned by the Calendar service that
	 *   contains a collection of event entries.
	 */
	handleEventsFeed: function (myResultsFeedRoot) {
		var crtDate = null, crtProject = null,
			myEventsFeed, events, i = 0, s,
			crtEvent, eventTimes, aStartTime, aEndTime, eventTitle, aHoursDiff;
		app.calendar.doOnReset();
		myEventsFeed = myResultsFeedRoot.feed;
		events = myEventsFeed.getEntries();
		for (i = 0; i < events.length; i = i + 1) {
			crtEvent = events[i];
			// filter out deleted events !!!
			if (crtEvent.getEventStatus().value === "http://schemas.google.com/g/2005#event.canceled") { continue; }
			if (crtEvent.getTimes()[0] === undefined || crtEvent.getTimes()[0] === null || crtEvent.getTimes()[0].getStartTime().dateOnly) { continue; }
			eventTimes = crtEvent.getTimes()[0];
			if (crtDate === null || crtDate.getDate() !== eventTimes.getStartTime().getDate().getDate()) {
				crtProject = null;
				crtDate = eventTimes.getStartTime().getDate();
			}
			if (crtProject === null || crtProject !== crtEvent.getTitle().getText().split(" ")[0]) {
				s = crtEvent.getTitle().getText().split("-");
				if (s.length === 1) {
					s = crtEvent.getTitle().getText().split(" ");
				}
				crtProject = s[0];
			}
			aStartTime = eventTimes.getStartTime().getDate().toTime();
			aEndTime = eventTimes.getEndTime().getDate().toTime();
			aHoursDiff = eventTimes.getEndTime().getDate().hoursDiff(eventTimes.getStartTime().getDate());
			//remove "[ProjectName] - " prefix. to improve !!!
			eventTitle = crtEvent.getTitle().getText().substr(crtProject.length + 1);
			eventTitle += " (" + aStartTime + " - " + aEndTime + " = " + aHoursDiff + " h)";
			modData.addTask({
				startDate: crtDate, 
				project: crtProject, 
				description: eventTitle, 
				startTime: aStartTime, 
				endTime: aEndTime, 
				hours: aHoursDiff
			});
		}
		app.calendar.doAfterGetEvents(modData.sheets);
	},
	//event functions to be attached by app start
	doOnReset: null,
	doOnHandleError: null,
	doAfterGetEvents: null
};

var modSecurity = {
	// Requests an AuthSub token for interaction with the Calendar service.
	login: function () {
		app.security.logout();
		var token = google.accounts.user.login(app.configuration.EVENT_FEED_URL, app.configuration.HOSTED_DOMAIN);
	},
	//Revokes the AuthSub token and resets the page.
	logout: function () {
		if (google.accounts.user.checkLogin(app.configuration.EVENT_FEED_URL)) {
			google.accounts.user.logout();
		}
		app.calendar.init();
	}
};

var modPresentation = {
	bindEvents: function () {
		$(app.constants.LOGIN).bind("click", function () { app.security.login(); });
		$(app.constants.LOGOUT).bind("click", function () { app.security.logout(); });
		$(app.constants.PREV_WEEK).bind("click", function () { app.presentation.showPreviousWeek(); });
		$(app.constants.NEXT_WEEK).bind("click", function () { app.presentation.showNextWeek(); });
	},
	showCurrentWeek: function () {
		var firstDate, lastDate;
		if (modPresentation.currentDate === undefined) { this.currentDate = new Date(); }
		firstDate = app.presentation.currentDate.getFirstDayOfWeek(app.presentation.currentDate).clearTime();    
		lastDate = firstDate.addDays(7).clearTime();
		$(app.constants.CRT_WEEK).html("Week " + app.presentation.currentDate.getISOWeek() + " (" + firstDate.getISODate() + " to " + lastDate.getISODate() + ")");
		$(app.constants.PREV_WEEK).html("&lt; Week " + (app.presentation.currentDate.getISOWeek() - 1));
		$(app.constants.NEXT_WEEK).html("Week " + (app.presentation.currentDate.getISOWeek() + 1) + " &gt;");
		app.presentation.doOnGetEvents(firstDate, lastDate);
	},	
	showPreviousWeek: function () {
		app.presentation.currentDate = app.presentation.currentDate.addDays(-7);
		app.presentation.showCurrentWeek();
	},	
	showNextWeek: function () {
		app.presentation.currentDate = app.presentation.currentDate.addDays(7);
		app.presentation.showCurrentWeek();
	},
	/**
	 * Resets the form back to the same state as when the page first loads. 
	 */
	reset: function () {
		$(app.constants.EVENTS).empty();
	},
	/**
	 * Creates a popup alert to notify the user of a Google data related error.
	 * 
	 * @param {Object} An error that occurred while attempting to interact with the Google Calendar service.  
	 */
	handleError: function (e) {
		if (e instanceof Error) {
			// Alert with the error line number, file and message.
			this.window.alert('Error at line ' + e.lineNumber +	' in ' + e.fileName + '\n' + 'Message: ' + e.message);
			// If available, output HTTP error code and status text
			if (e.cause) { this.window.alert('Root cause: HTTP error ' + e.cause.status + ' with status text of: ' + e.cause.statusText); }
		} else { this.window.alert(e.toString()); }
	},
	addDate: function (crtSheet) {
		var dayHours = 0, p, t;
		for (p = 0; p < crtSheet.projects.length; p = p + 1) {
			for (t = 0; t < crtSheet.projects[p].tasks.length; t = t + 1) {
				dayHours += crtSheet.projects[p].tasks[t].hours;
			}
		}		
		$(app.constants.EVENTS).append("<br/><u>" + crtSheet.sheetDate.getMDayName() + ", " + crtSheet.sheetDate.getISODate() + " = " + dayHours + "h</u><br/><br/>");		
		return dayHours;
	},
	addProject: function (crtProject) {
		$(app.constants.EVENTS).append('<div><span class="project">' + crtProject + '</span></div>');
	},
	addProjectTasks: function (crtProject) {
		var hours = 0, i;
		//$(app.constants.EVENTS).append('<div class="project-tasks">');
		for (i = crtProject.tasks.length - 1; i >= 0; i = i - 1) {
			$(app.constants.EVENTS).append(crtProject.tasks[i].description);
			$(app.constants.EVENTS).append("<br/>");
			hours += crtProject.tasks[i].hours;
		}
		//add project hours
		$(app.constants.EVENTS).append("= " + hours + " h");
		$(app.constants.EVENTS).append("<br/>");
	},
	showTimeSheets: function (sheets) {
		var weekHours = 0, s, p;
		for (s = 0; s < modData.sheets.length; s = s + 1) {
			if (modData.sheets[s].projects.length > 0) {
				weekHours += modPresentation.addDate(modData.sheets[s]);
				for (p = 0; p < modData.sheets[s].projects.length; p = p + 1) {
					modPresentation.addProject(modData.sheets[s].projects[p].name);
					modPresentation.addProjectTasks(modData.sheets[s].projects[p]);
				}
			}
		}
		
		$(app.constants.EVENTS).append("<br/>");
		$(app.constants.CRT_WEEK_TOTAL).html(" = " + weekHours + "h");
	},
	//events to be attached by app start
	doOnGetEvents: null
};

var app = {
	//attach app modules
	presentation: modPresentation,
	constants: modConstants,
	configuration: modConfiguration,
	security: modSecurity,
	calendar: modCalendar,
	data: modData,
	start: function () {
		$(app.constants.VERSION_SPAN).html("v" + app.constants.VERSION);
		app.presentation.bindEvents();
		app.presentation.doOnGetEvents = app.calendar.getEvents;
		app.calendar.doOnReset = app.presentation.reset;
		app.calendar.doOnHandleError = app.presentation.handleError;
		app.calendar.doAfterGetEvents = app.presentation.showTimeSheets;
		app.calendar.init();
	}
};

google.load("gdata", "2.x", {packages: ["calendar"]});
//google.load("jquery", "1.7.1");
google.setOnLoadCallback(app.start);
